package com.singfan.app.controller;

import com.singfan.app.controller.model.RequestModelBase;
import com.singfan.app.controller.model.ResponseModelBase;
import com.singfan.app.controller.model.response.ResultCode;
import com.singfan.app.util.AppleUtil;
import com.singfan.common.util.JsonUtil;
import com.singfan.common.util.StringUtil;
import com.singfan.core.LoggerFactory;
import com.singfan.core.component.BarberTokenHolder;
import com.singfan.core.component.CustomerTokenHolder;
import com.singfan.core.component.ShopTokenHolder;
import com.singfan.core.dao.database.model.BarberToken;
import com.singfan.core.dao.database.model.CustomerToken;
import com.singfan.core.dao.database.model.ShopToken;
import com.singfan.core.service.IBarberService;
import com.singfan.core.service.ICustomerService;
import com.singfan.core.service.IShopService;
import com.singfan.core.util.AccessTokenUtil;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;

/**
 * Author: hansonghe
 * Date: 2015-08-28 17:06
 * Desc:
 */
public abstract class SingFanControllerBase {
    private static final String[] headerNames = new String[]{"x-forwarded-for", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
    protected Logger traceLog = LoggerFactory.getTraceLogger();
    @Autowired
    protected ICustomerService customerService;
    @Autowired
    protected IBarberService barberService;
    @Autowired
    protected IShopService shopService;

    protected <T extends RequestModelBase> T request2Bean(HttpServletRequest request, Class<T> beanClass){
        T t = null;
        try {
            String httpRequestBody = getBodyString(request.getReader());

            CustomerToken customerToken = null;
            BarberToken barberToken = null;
            ShopToken shopToken = null;

            if(!StringUtil.isNullOrEmpty(httpRequestBody)){
                t = JsonUtil.json2Bean(httpRequestBody, beanClass);
                String accessToken = t.getAccessToken();
                if (AccessTokenUtil.isCustomerAccessToken(accessToken)){
                    customerToken = customerService.findCustomerToken(accessToken);
                }
                else if (AccessTokenUtil.isBarberAccessToken(accessToken)){
                    barberToken = barberService.findBarberToken(accessToken);
                }
                else if (AccessTokenUtil.isShopAccessToken(accessToken)){
                    shopToken = shopService.findShopToken(accessToken);
                }

            }
            else {
                t = beanClass.newInstance();
            }

            CustomerTokenHolder.bindCustomerToken(customerToken);
            BarberTokenHolder.bindBarberToken(barberToken);
            ShopTokenHolder.bindShopToken(shopToken);
        }
        catch (Exception e) {
            traceLog.error(String.format("%1$s.request2Bean() error -> %2$s", this.getClass().getSimpleName(), e.getMessage()));
            e.printStackTrace();
        }

        return t;
    }

    protected void bean2Response(HttpServletResponse response, ResponseModelBase responseModel){
        try {
            if (!AppleUtil.isPassed()){
                if (responseModel.getResultCode() == ResultCode.ACCESS_TOKEN_ERROR){
                    responseModel.setResultCode(ResultCode.USER_APPLE_TOKEN_ERROR);
                }
            }

            CustomerTokenHolder.unbindCustomerToken();
            BarberTokenHolder.unbindBarberToken();
            ShopTokenHolder.unbindShopToken();

            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
            String responseText = JsonUtil.bean2Json(responseModel);
            response.getWriter().write(responseText);
        }
        catch (IOException e) {
            traceLog.error(String.format("%1$s.bean2Response() error -> %2$s", this.getClass().getSimpleName(), e.getMessage()));
            e.printStackTrace();
        }
    }

    protected String getIpAddress(HttpServletRequest request) {
        String qVia = request.getHeader("QVia");
        String ip;
        if (qVia != null && isValidQVia(qVia)) {
            StringBuilder ipBuilder = new StringBuilder();
            String ip_segment;
            for (int i = 0; i < 8; i += 2) {
                ip_segment = qVia.substring(i, i + 2);
                ipBuilder.append(Integer.valueOf(ip_segment, 16));
                if (i != 6) {
                    ipBuilder.append(".");
                }
            }
            ip = ipBuilder.toString();
        } else {
            ip = getClientIp(request);
        }
        return ip;
    }

    @ExceptionHandler
    public void exceptionHandler(HttpServletRequest request, Exception ex) {
        traceLog.error(String.format("%1$s.exceptionHandler() error -> %2$s", this.getClass().getSimpleName(), ex.getMessage()));
    }

    private static boolean isValidQVia(String ip) {
        return (!(ip == null || ip.length() != 40));
    }

    private static String getClientIp(HttpServletRequest request) {
        for (String name : headerNames) {
            String ip = request.getHeader(name);
            if (isValidString(ip)) {
                return ip;
            }
        }
        return request.getRemoteAddr();
    }

    private static boolean isValidString(String str) {
        boolean result = false;

        if(str != null){
            str = str.trim().toLowerCase();
            if(!str.equals("") && !str.equals("null") && !str.equals("unknown")){
                result = true;
            }
        }

        return result;
    }

    private static String getBodyString(BufferedReader br) throws IOException {
        String inputLine;
        String str = "";
        while ((inputLine = br.readLine()) != null) {
            str += inputLine;
        }
        br.close();
        return str;
    }
}
